﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using KGE.Define;
using KGE.Resource;

namespace KGE
{
    public class GPU
    {
        Game game;

        System.Drawing.Font Font;
        PresentParameters PPs;
        Device d3DDev;
        CustomVertex.PositionColoredTextured[] Vertexs = new CustomVertex.PositionColoredTextured[6];

        Size Resolution
        {
            get
            {
                return new Size(d3DDev.PresentationParameters.BackBufferWidth, d3DDev.PresentationParameters.BackBufferHeight);
            }
        }

        void intiViewport(Vector3 location, Vector3 lookAt)
        {
            location.Z = -(float)((double)Resolution.Height / 2.0 / Math.Tan(location.Z * Math.PI / 180.0));

            d3DDev.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float)Resolution.Width / (float)Resolution.Height, -100, 100);
            d3DDev.Transform.View = Matrix.LookAtLH(location, lookAt, new Vector3(0, 1, 0));
        }

        void intiD3DDev()
        {
            intiViewport(new Vector3(0, 0, 22.5f), new Vector3());
            //d3DDev.RenderState.CullMode = Cull.None;
            d3DDev.RenderState.AlphaBlendEnable = true;
            d3DDev.RenderState.SourceBlend = Microsoft.DirectX.Direct3D.Blend.SourceAlpha;
            d3DDev.RenderState.DestinationBlend = Microsoft.DirectX.Direct3D.Blend.InvSourceAlpha;
            d3DDev.RenderState.Lighting = false;
            d3DDev.TextureState[0].AlphaOperation = TextureOperation.Modulate;
            d3DDev.VertexFormat = CustomVertex.PositionColoredTextured.Format;
        }

        void OnD3DDevDeviceResizing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = true;
        }

        void OnD3DDevDeviceLost(object sender, EventArgs e)
        {
            //throw new NotImplementedException();
            //if (Vertexs != null)
            //{
            //    Vertexs = null;
            //}
        }

        void OnD3DDevDeviceReset(object sender, EventArgs e)
        {
            intiD3DDev();
        }

        void drawTexture(Texture texture, Color filterColor, Size textureSize, Rectangle srcRect, Size size, Vector3 v3)
        {
            drawTexture(texture, false, filterColor, textureSize, srcRect, size, v3);
        }
        void drawTexture(Texture texture, bool isFlip, Color filterColor, Size textureSize, Rectangle srcRect, Size size, Vector3 v3)
        {
            float x = v3.X;
            float y = v3.Y;
            float z = v3.Z;
            //float x = 0;
            //float y = 0;
            //float z = 0;

            float fu = 0f;
            float fv = 0f;
            float tu = 1f;
            float tv = 1f;

            int color_int = filterColor.ToArgb();

            int width = size.Width;
            int height = size.Height;

            //textureSize.Width = texture.GetSurfaceLevel(0).Description.Width;
            //textureSize.Height = texture.GetSurfaceLevel(0).Description.Height;
            fu = (0.5f + (float)srcRect.Left) / (float)textureSize.Width;
            fv = (0.5f + (float)srcRect.Top) / (float)textureSize.Height;
            tu = (0.0f + (float)srcRect.Right) / (float)textureSize.Width;
            tv = (0.0f + (float)srcRect.Bottom) / (float)textureSize.Height;

            if (isFlip)
            {
                fu = -fu;
                tu = -tu;
            }

            x -= Resolution.Width / 2;
            y -= Resolution.Height / 2;
            y = -y;

            //Vertexs[0] = new CustomVertex.PositionColoredTextured(x, y, z, color_int, fu, fv);
            //Vertexs[1] = new CustomVertex.PositionColoredTextured(x + width, y - height, z, color_int, tu, tv);
            //Vertexs[2] = new CustomVertex.PositionColoredTextured(x, y - height, z, color_int, fu, tv);

            //Vertexs[3] = new CustomVertex.PositionColoredTextured(x, y, z, color_int, fu, fv);
            //Vertexs[4] = new CustomVertex.PositionColoredTextured(x + width, y, z, color_int, tu, fv);
            //Vertexs[5] = new CustomVertex.PositionColoredTextured(x + width, y - height, z, color_int, tu, tv);

            Vertexs[0].X = x;
            Vertexs[0].Y = y;
            Vertexs[0].Z = z;
            Vertexs[0].Tu = fu;
            Vertexs[0].Tv = fv;
            Vertexs[0].Color = color_int;

            Vertexs[1].X = x + width;
            Vertexs[1].Y = y - height;
            Vertexs[1].Z = z;
            Vertexs[1].Tu = tu;
            Vertexs[1].Tv = tv;
            Vertexs[1].Color = color_int;

            Vertexs[2].X = x;
            Vertexs[2].Y = y - height;
            Vertexs[2].Z = z;
            Vertexs[2].Tu = fu;
            Vertexs[2].Tv = tv;
            Vertexs[2].Color = color_int;

            Vertexs[3].X = x;
            Vertexs[3].Y = y;
            Vertexs[3].Z = z;
            Vertexs[3].Tu = fu;
            Vertexs[3].Tv = fv;
            Vertexs[3].Color = color_int;

            Vertexs[4].X = x + width;
            Vertexs[4].Y = y;
            Vertexs[4].Z = z;
            Vertexs[4].Tu = tu;
            Vertexs[4].Tv = fv;
            Vertexs[4].Color = color_int;

            Vertexs[5].X = x + width;
            Vertexs[5].Y = y - height;
            Vertexs[5].Z = z;
            Vertexs[5].Tu = tu;
            Vertexs[5].Tv = tv;
            Vertexs[5].Color = color_int;

            d3DDev.SetTexture(0, texture);
            d3DDev.DrawUserPrimitives(PrimitiveType.TriangleList, 2, Vertexs);
            //d3DDev.Transform.World = Matrix.Translation(v3);
        }

        public GPU(Game g)
        {
            game = g;
            PPs = new Microsoft.DirectX.Direct3D.PresentParameters();
            PPs.SwapEffect = Microsoft.DirectX.Direct3D.SwapEffect.Discard;
            PPs.Windowed = true;
            PPs.EnableAutoDepthStencil = false;
            PPs.BackBufferWidth = game.GameControl.Width;
            PPs.BackBufferHeight = game.GameControl.Height;
            PPs.BackBufferCount = 1;
            PPs.BackBufferFormat = Format.A8R8G8B8;
            PPs.PresentationInterval = PresentInterval.One;

            d3DDev = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, game.GameControl, Microsoft.DirectX.Direct3D.CreateFlags.SoftwareVertexProcessing, PPs);

            d3DDev.DeviceResizing += new System.ComponentModel.CancelEventHandler(OnD3DDevDeviceResizing);
            d3DDev.DeviceReset += new EventHandler(OnD3DDevDeviceReset);
            d3DDev.DeviceLost += new EventHandler(OnD3DDevDeviceLost);

            //VertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColoredTextured), 6, d3DDev, Usage.None, CustomVertex.PositionColoredTextured.Format, Pool.Managed);
            //d3DDev.SetStreamSource(0, VertexBuffer, 0);

            //Surface = new Texture(d3DDev, game.Options.WindowSizePixel.Width, game.Options.WindowSizePixel.Height, 0, Usage.None, Format.A8R8G8B8, Pool.Managed);
            //Pen = Surface.GetSurfaceLevel(0).GetGraphics();

            //Pen.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
            //Pen.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

            intiD3DDev();

            Font = new System.Drawing.Font("微软雅黑", 16, GraphicsUnit.Pixel);


        }

        public void DrawText(string text, Color color, Vector3 v3)
        {
            DrawText(text, new SolidBrush(color), v3);
        }
        public void DrawText(string text, Brush brush, Vector3 v3)
        {
            DrawText(text, brush, Font, v3);
        }
        public void DrawText(string text, Brush brush, System.Drawing.Font font, Vector3 v3)
        {
            if (font != Font)
            {
                Font.Dispose();
                Font = font;
            }

            string[] texts = text.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.None);
            int longest = 0;
            foreach (string str in texts)
            {
                if (str.Length > longest)
                {
                    longest = str.Length;
                }
            }

            Size tsize = new Size();

            tsize.Width = longest * (int)font.Height;
            tsize.Height = texts.Length * (int)font.Height;

            if (tsize.Width > Resolution.Width)
            {
                tsize.Width = Resolution.Width;
            }
            if (tsize.Height > Resolution.Height)
            {
                tsize.Height = Resolution.Height;
            }

            using (Texture texture = new Texture(d3DDev, tsize.Width, tsize.Height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed))
            {
                using (Graphics g = texture.GetSurfaceLevel(0).GetGraphics())
                {
                    //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
                    //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                    for (int i = 0; i < texts.Length; i++)
                    {
                        g.DrawString(texts[i], font, brush, 0, i * font.Height);
                    }
                }
                drawTexture(texture, Color.White, tsize, new Rectangle(new Point(), tsize), tsize, v3);
            }
        }
        public void DrawRectangle(Color color, Rectangle rect)
        {
            DrawRectangle(new Pen(color), rect);
        }
        public void DrawRectangle(Pen pen, Rectangle rect)
        {
            Size tsize = Util.GetFitSize(rect.Size);
            using (Texture texture = new Texture(d3DDev, tsize.Width, tsize.Height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed))
            {
                using (Graphics g = texture.GetSurfaceLevel(0).GetGraphics())
                {
                    //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
                    //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                    g.DrawRectangle(pen, new Rectangle(new Point(), rect.Size));
                }
                drawTexture(texture, Color.White, tsize, new Rectangle(new Point(),tsize), tsize, Util.Point2Vector3(rect.Location));
            }
        }

        public void DrawKImage(KImage img, Color filterColor, Vector3 v3)
        {
            if (img.Texture == null)
            {
                img.Texture = KImage2Texture(img);
            }
            Vector3 v33 = Util.Vector3SubVector3(Util.Point2Vector3(img.DrawRectangle.Location), Util.Point2Vector3(img.OPoint));
            if (img.IsHorizontalFlip)
            {
                v33.X = -v33.X;
                v33.X -= img.DrawRectangle.Width;
            }

            v3 = Util.Vector3AddVector3(v3, v33);
            Rectangle rect = new Rectangle(new Point(), img.DrawRectangle.Size);
            drawTexture(img.Texture, img.IsHorizontalFlip, filterColor, img.DrawRectangle.Size, rect, img.DrawRectangle.Size, v3);
        }

        public void DrawKMovie(KMovie mov, Color filterColor, Vector3 v3)
        {
            double now = game.Devices.Timer.TotalSeconds;
            double ts = now - mov.LastUpdateTime;
            if (ts > mov.CurrentFrame.Length)
            {
                mov.LastUpdateTime = now;
                mov.FrameIncrease();
            }
            DrawKImage(mov.CurrentFrame.KImage, filterColor, v3);
        }

        public void Clear()
        {
            d3DDev.Clear(Microsoft.DirectX.Direct3D.ClearFlags.Target, 0, 1f, 0);
            d3DDev.BeginScene();
        }
        public void Present()
        {
            d3DDev.EndScene();
            try
            {
                d3DDev.Present();
            }
            catch { }
        }
        public bool Windowed
        {
            set
            {
                d3DDev.PresentationParameters.Windowed = value;
            }
            get
            {
                return d3DDev.PresentationParameters.Windowed;
            }
        }
        public void Reset()
        {
            d3DDev.Reset(d3DDev.PresentationParameters);
        }
        public void Dispoise()
        {
            Font.Dispose();
            //d3DDev.Dispose();
        }

        public Texture KImage2Texture(KImage ki)
        {
            Texture texture = null;
            Size s = Util.GetFitSize(ki.DrawRectangle.Size);
            ki.DrawRectangle.Size = s;
            texture = new Texture(d3DDev, s.Width, s.Height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed);
            using (Graphics g = texture.GetSurfaceLevel(0).GetGraphics())
            {
                //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
                //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                g.DrawImage(Image.FromStream(ki.DataStream), new Point());
            }
            return texture;
        }
    }
}